﻿using Dapper;
using System;
using System.Data;

namespace PageAdmin.Utils.ORM
{
    /// <summary>
    /// 数据库连接对象,可通过继承使用，单机项目上可做成单例，web项目建议每次new一个新的对象。
    /// 如果需要使用事务，建议DataBaseContext对象在using块中实例化，避免因为忘记提交事务或出错未回滚导致连接得不到释放。
    /// </summary>
    public class DataBaseContext : IDisposable
    {
        private IDbInstance _DbInstance;
        private string _Schema;
        private string _ProviderName;
        private string _ConnectionString;
        private DataBaseType _DbType = 0;
        private double _CacheMinutes;
        private TransactionStateEnum _TransactionState;


        /// <summary>
        /// 带参构造函数
        /// </summary>
        /// <param name="providerName">驱动程序</param>
        /// <param name="connectionString">sql连接字符</param>
        /// <param name="cacheMinutes">数据缓存默认保存分钟数,缓存采用System.Runtime.Caching.MemoryCache.Default</param>
        public DataBaseContext(string providerName, string connectionString, double cacheMinutes = 0)
        {
            _TransactionState = TransactionStateEnum.NotEnabled;
            _ProviderName = providerName;
            _ConnectionString = connectionString;
            _CacheMinutes = cacheMinutes;
            if (_CacheMinutes <= 0)
            {
                _CacheMinutes = 30;//未设置则默认30分钟
            }
        }

        /// <summary>
        /// 带参构造函数
        /// </summary>
        /// <param name="providerName">驱动程序</param>
        /// <param name="connectionString">sql连接字符</param>
        /// <param name="cacheMinutes">数据缓存默认保存分钟数,缓存采用System.Runtime.Caching.MemoryCache.Default</param>
        public DataBaseContext(string providerName,string connectionString,string schema,double cacheMinutes = 0)
        {
            _TransactionState = TransactionStateEnum.NotEnabled;
            _ProviderName = providerName;
            _ConnectionString = connectionString;
            _CacheMinutes = cacheMinutes;
            this._Schema = schema;
            if (_CacheMinutes <= 0)
            {
                _CacheMinutes = 30;//未设置则默认30分钟
            }
        }

        /// <summary>
        /// 开启事务，开启事务执行的任何sql都不会更新数据库（但是可以查询），直到运行TransactionCommit提交事务后才会更新数据库。
        /// </summary>
        public IDbTransaction TransactionBegin()
        {
            if(_TransactionState==TransactionStateEnum.Beign)
            {
                throw new Exception("当前事务已开启，请勿重复调用TransactionBegin方法");
            }
            DbInstance.TransactionBegin();
            _TransactionState = TransactionStateEnum.Beign;
            return DbInstance.DbTransaction;
        }

        /// <summary>
        /// 提交事务。
        /// </summary>
        public void TransactionCommit()
        {
            if (_TransactionState == TransactionStateEnum.Commited)
            {
                throw new Exception("当前事务已经提交，请勿重复调用TransactionCommit方法");
            }
            else if(_TransactionState != TransactionStateEnum.Beign)
            {
                throw new Exception("当前事务还未开启，请先调用TransactionBegin方法");
            }
            DbInstance.TransactionCommit();
            _TransactionState = TransactionStateEnum.Commited;
        }

        /// <summary>
        /// 事务回滚，用于取消事务，同时会关闭连接。
        /// </summary>
        public void TransactionRollback()
        {
            DbInstance.TransactionRollback();
            _TransactionState = TransactionStateEnum.RollBack;
        }


        /// <summary>
        /// 缓存时间
        /// </summary>
        public double CacheMinutes
        {
            get
            {
                return _CacheMinutes;
            }
            set
            {
                _CacheMinutes = value;
            }
        }

        /// <summary>
        ///表架构，不设置则不限制，默认用户名，如果没有则默认dbo
        /// </summary>
        public string Schema
        {
            get
            {
                return _Schema;
            }
            set
            {
                _Schema = value;
            }
        }

        public int? CommandTimeout { get; set; } = null;

        /// <summary>
        /// 实体和数据表的映射方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public DbSet<T> DbSet<T>() where T : class, new()
        {
            return new DbSet<T>(this.DbConnection, this.Schema, this.DbTransaction, this.CommandTimeout, this.DbParamterList, this.CacheMinutes, this.ExecutedInitDbParamter);
        }

        /// <summary>
        /// 数据库类型实例,一个dbContext公用一个DbInstance数据库实例
        /// </summary>
        internal IDbInstance DbInstance
        {
            get
            {
                if (_DbInstance == null)
                {
                    _DbInstance = new DbSqlServer(_ProviderName, _ConnectionString, this.CommandTimeout);//DbFactory.CreateDbInstance(DbType);
                }
                return _DbInstance;
            }
        }

        /// <summary>
        /// 或者数据库的基本信息，如服务器地址，数据库名，用户名或密码
        /// </summary>
        public DataBaseInfo DbInfo
        {
            get
            {
                return DbInstance.CurrentDataBaseInfo;
            }
        }

        /// <summary>
        /// 获取当前连接的事务
        /// </summary>
        public IDbTransaction DbTransaction
        {
            get
            {
                return DbInstance.DbTransaction;
            }
        }

        /// <summary>
        /// 获取当前实例使用的数据库连接实例
        /// </summary>
        public IDbConnection DbConnection
        {
            get
            {
                return DbInstance.Conn;
            }
        }

        /// <summary>
        /// 测试连接是否正常
        /// </summary>
        public bool ConnectionEnabled
        {
            get
            {
                bool connEnabled = false;
                try
                {
                    this.DbConnection.Open();
                    connEnabled = true;
                }
                catch
                {
                    connEnabled = false;
                }
                finally
                {
                    this.DbConnection.Close();
                }
                return connEnabled;
            }

        }

        /// <summary>
        /// 获取当前实例的事务状态
        /// </summary>
        public TransactionStateEnum TransactionState
        {
            get
            {
                return _TransactionState;
            }
        }

        /// <summary>
        /// 执行完任何sql语句是否都是把DbParamter初始化为null，默认为true;
        /// </summary>
        public bool ExecutedInitDbParamter { get; set; } = true;

        /// <summary>
        /// 获取当前实例使用的数据库类型
        /// </summary>
        public DataBaseType DbType
        {
            get
            {
                if (_DbType == 0)
                {
                    _DbType = GetDataBaseType(_ProviderName);
                }
                return _DbType;
            }
        }



        /// <summary>
        /// Sql参数化用到的参数对象。
        /// </summary>
        internal DynamicParameters DbParamterList { get; set; } = null;

        /// <summary>
        /// 添加sql参数
        /// </summary>
        /// <param name="paramterName">参数名称，如@username</param>
        /// <param name="value">参数值</param>
        /// <param name="size"></param>
        public void AddDbParamter(string paramterName, object value, int size = -1)
        {
            if (this.DbParamterList == null)
            {
                this.DbParamterList =new DynamicParameters();
            }
            DbParamterList.Add(paramterName, value,null,null,size);
        }

        /// <summary>
        /// 设置sql参数，如果需要多次添加参数，请使用AddParameter方法
        /// </summary>
        /// <param name="paramterName"></param>
        /// <param name="value"></param>
        /// <param name="size"></param>
        public void SetDbParamter(string paramterName, object value, int size = -1)
        {
            this.DbParamterList = null;
            AddDbParamter(paramterName, value, size);
        }

        /// <summary>
        /// 添加sql参数，可以传递匿名对象，如果需要多次添加参数，请使用AddParameter方法
        /// </summary>
        /// <param name="obj">匿名对象</param>
        public void AddDbParamter(object obj)
        {
            if (obj == null)
            {
                return;
            }
            //Type type = obj.GetType();
            var properties = obj.GetType().GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                object value = properties[i].GetValue(obj);
                if (value == null)
                {
                    value = DBNull.Value;
                }
                AddDbParamter(properties[i].Name, value);
            }
        }
        /// <summary>
        /// 设置sql参数，可以传递匿名对象，如果需要多次添加参数，请使用AddParameter方法
        /// </summary>
        /// <param name="obj">匿名对象</param>
        public void SetDbParamter(object obj)
        {
            this.DbParamterList = null;
            if (obj == null)
            {
                return;
            }
            AddDbParamter(obj);
        }

        /// <summary>
        /// 任何sql语句执行完后都会调用
        /// </summary>
        internal void ExecutedHandel()
        {
            if (ExecutedInitDbParamter)
            {
                this.DbParamterList = null;
            }
        }

        /// <summary>
        /// 获取数据库类型
        /// </summary>
        /// <param name="providerName"></param>
        /// <returns></returns>
        private DataBaseType GetDataBaseType(string providerName)
        {
            DataBaseType dataBaseType = DataBaseType.MsSql;
            if (providerName.IndexOf("Microsoft.", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                dataBaseType = DataBaseType.Access;
            }
            else if (providerName.IndexOf("System.Data.OracleClient", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                dataBaseType = DataBaseType.Oracle;
            }
            else if (providerName.IndexOf("MySql.Data.MySqlClient", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                dataBaseType = DataBaseType.MySql;
            }
            return dataBaseType;
        }

        /// <summary>
        /// 释放当前对象应用的DbConnection连接。
        /// </summary>
        public void Dispose()
        {
            if(_TransactionState== TransactionStateEnum.Beign)
            {
                TransactionRollback();
            }
            _TransactionState = TransactionStateEnum.NotEnabled;
            DbInstance.Dispose();
        }

    }
}